package com.geekhalo.info.domain.info.picture;

import com.geekhalo.info.domain.category.InfoCategory;
import com.geekhalo.info.domain.category.exception.InfoCategoryNotFoundException;
import com.geekhalo.info.domain.info.BaseInfo;
import com.geekhalo.info.domain.info.picture.create.CreatePictureInfoCommand;
import com.geekhalo.info.domain.info.picture.create.CreatePictureInfoContext;
import com.geekhalo.info.domain.info.picture.create.PictureInfoCreatedEvent;
import com.geekhalo.info.domain.info.picture.image.CreatePictureInfoImageCommand;
import com.geekhalo.info.domain.info.picture.image.PictureInfoImage;
import com.geekhalo.info.domain.info.picture.image.RemovePictureInfoImageCommand;
import com.geekhalo.info.domain.info.picture.image.UpdatePictureInfoImageCommand;
import com.geekhalo.info.domain.info.picture.offline.OfflinePictureInfoCommand;
import com.geekhalo.info.domain.info.picture.offline.OfflinePictureInfoContext;
import com.geekhalo.info.domain.info.picture.offline.PictureInfoOfflinedEvent;
import com.geekhalo.info.domain.info.picture.online.OnlinePictureInfoCommand;
import com.geekhalo.info.domain.info.picture.online.OnlinePictureInfoContext;
import com.geekhalo.info.domain.info.picture.online.PictureInfoOnlinedEvent;
import com.geekhalo.info.domain.info.picture.update.PictureInfoUpdatedEvent;
import com.geekhalo.info.domain.info.picture.update.UpdatePictureInfoCommand;
import com.geekhalo.info.domain.info.picture.update.UpdatePictureInfoContext;
import com.geekhalo.lego.core.command.support.AbstractAggRoot;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Entity
@Table(name = "picture_info")
@PrimaryKeyJoinColumn(name = "info_id")
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Setter(AccessLevel.PRIVATE)
public class PictureInfo extends BaseInfo {

    private Integer imageCount;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "info_id")
    private List<PictureInfoImage> images = new ArrayList<>();

    public static PictureInfo create(CreatePictureInfoContext context) {
        Optional<InfoCategory> categoryOpt = context.getCategory();
        if (!categoryOpt.isPresent()){
            throw new InfoCategoryNotFoundException();
        }
        InfoCategory infoCategory = categoryOpt.get();
        infoCategory.checkCanCreateInfo();

        CreatePictureInfoCommand command = context.getCommand();
        PictureInfo pictureInfo = new PictureInfo();
        pictureInfo.setCategoryId(infoCategory.getId());
        pictureInfo.setTitle(command.getTitle());

        List<CreatePictureInfoImageCommand> images = command.getImages();
        if (CollectionUtils.isNotEmpty(images)){
            List<PictureInfoImage> infoImages = images.stream()
                            .map(PictureInfoImage::create)
                            .collect(Collectors.toList());
            pictureInfo.updateImages(infoImages);
        }

        pictureInfo.init();
        return pictureInfo;
    }


    private void updateImages(List<PictureInfoImage> infoImages) {
        if (infoImages == null){
            setImageCount(0);
        }else {
            this.images.clear();
            this.images.addAll(infoImages);
        }
        updateImageCount();
    }

    private void updateImageCount() {
        setImageCount(this.images.size());
    }

    private void init(){
        setOnline();
        addEvent(new PictureInfoCreatedEvent(this));
    }

    public void online(OnlinePictureInfoContext context) {
        if (setOnline()) {
            addEvent(new PictureInfoOnlinedEvent(this));
        }
    }

    public void offline(OfflinePictureInfoContext context) {
        if (setOffline()) {
            addEvent(new PictureInfoOfflinedEvent(this));
        }
    }

    public void update(UpdatePictureInfoContext context) {
        UpdatePictureInfoCommand command = context.getCommand();
        if (StringUtils.isNotEmpty(command.getTitle())){
            setTitle(command.getTitle());
        }
        if (CollectionUtils.isNotEmpty(command.getRemoveImageCommands())){
            Set<Long> removeIds = command.getRemoveImageCommands().stream()
                    .map(RemovePictureInfoImageCommand::getImageId)
                    .collect(Collectors.toSet());
            this.images.removeIf(pictureInfoImage -> removeIds.contains(pictureInfoImage.getId()));
        }

        if (CollectionUtils.isNotEmpty(command.getUpdateImageCommands())){
            Map<Long, PictureInfoImage> imageMap = this.images.stream()
                    .collect(Collectors.toMap(PictureInfoImage::getId, Function.identity()));
            for (UpdatePictureInfoImageCommand updateCommand : command.getUpdateImageCommands()){
                PictureInfoImage image = imageMap.get(updateCommand.getImageId());
                if (image != null){
                    image.updateBy(updateCommand);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(command.getCreateImageCommands())){
            List<PictureInfoImage> nDatas = command.getCreateImageCommands().stream()
                    .map(PictureInfoImage::create)
                    .collect(Collectors.toList());
            this.images.addAll(nDatas);
        }
        updateImageCount();
        addEvent(new PictureInfoUpdatedEvent(this));
    }
}